package pers.cz.encrypt;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;

import java.io.UnsupportedEncodingException;

/**
 * @program: Reids
 * @description: PwdHelper
 * @author: Cheng Zhi
 * @create: 2022-03-10 10:50
 **/
public class PwdHelper {

    private static PwdIntf pwdIntf = new AESECB256Impl();

    private PwdHelper() {
        // avoid being instantiated
    }

    static {
        init();
    }

    private static void init() {
        String type = System.getProperty("pwd.ecrypt");
        if ("3DES".equals(type)) {
            pwdIntf = new TripleDESImpl();
        } else { // default = aes
            pwdIntf = new AESECB256Impl();
        }
    }

    /**
     * 数据库密码解密
     */
    public static String decrypt(String pass) {
        return pwdIntf.decrypt(pass);
    }

    /**
     * 数据库密码加密
     */
    public static String ecrypt(String pass) {
        return pwdIntf.ecrypt(pass);
    }

    public static interface PwdIntf {
        public String decrypt(String pass);
        public String ecrypt(String pass);
    }

    /**
     * 3des 实现
     *
     */
    protected static class TripleDESImpl implements PwdIntf{
        /**
         * 获取数据库加密的密钥,目前使用固定密钥
         */
        private static byte[] getEncryptKey() {
            byte[] secKry;
            try {
                secKry = "781296-5e32-89122".getBytes("US-ASCII");
                return secKry;
            } catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException(e);
            }
        }

        @Override
        public String ecrypt(String pass) {
            TripleDES t = new TripleDES();
            return t.cipher2(getEncryptKey(), pass);
        }

        @Override
        public String decrypt(String pass) {
            TripleDES t = new TripleDES();
            return t.decipher2(getEncryptKey(), pass);
        }
    }

    /**
     * AES 实现
     * AES-256-ECB-ZeroBytePadding
     */
    protected static class AESECB256Impl implements PwdIntf{

        AESECB256Impl(){
        }

        /**
         * 获取数据库加密的密钥,目前使用固定密钥
         */
        private static byte[] getEncryptKey() {
            byte[] secKry;
            try {
                secKry =("veris_billing_public_1234567890+").getBytes("US-ASCII");
                return secKry;
            } catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException(e);
            }
        }

        @Override
        public String ecrypt(String pass) {
            /*
             * This will use a supplied key, and encrypt the data
             * This is the equivalent of AES/ECB/ZeroBytePadding
             */
            BlockCipher engine = new AESEngine();
            // C++ 采用的方式是补0 ，这里采用ZeroBytePadding 和C++ 一样
            BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine, new ZeroBytePadding());

            byte[] key = getEncryptKey();
            byte[] input = pass.getBytes();

            cipher.init(true, new KeyParameter(key));

            byte[] cipherText = new byte[cipher.getOutputSize(input.length)];

            int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
            try {
                cipher.doFinal(cipherText, outputLen);
            } catch (CryptoException ce) {
                ce.printStackTrace();
            }
            return new String(Hex.encode(cipherText));
        }

        @Override
        public String decrypt(String pass) {
            /*
             * This will use a supplied key, and encrypt the data
             * This is the equivalent of AES/ECB/ZeroBytePadding
             */
            BlockCipher engine = new AESEngine();
            // C++ 采用的方式是补0 ，这里采用ZeroBytePadding 和C++ 一样
            BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(engine, new ZeroBytePadding());

            byte[] key = getEncryptKey();
            byte[] input = Hex.decode(pass);

            cipher.init(false, new KeyParameter(key));

            byte[] cipherText = new byte[cipher.getOutputSize(input.length)];

            int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
            try {
                cipher.doFinal(cipherText, outputLen);
            } catch (CryptoException ce) {
                ce.printStackTrace();
            }

            // 消除补零带来的影响
            int i =0;
            for (; i < cipherText.length; i++) {
                if(cipherText[i] == 0){
                    break;
                }
            }
            return new String(cipherText,0,i);
        }
    }


    public static void main(String[] args) {
        System.out.println(ecrypt("2ghlmcL_"));
        System.out.println(decrypt("64896d9d15117c867211396cca99509e"));
    }
}
